home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.hitl.washington.edu
/
ftp.hitl.washington.edu.tar
/
ftp.hitl.washington.edu
/
pub
/
people
/
tsoper
/
CT Explorer
/
OpenGLSliceView.cs
< prev
next >
Wrap
Text File
|
2005-06-12
|
13KB
|
519 lines
//This is a class containing an OpenGL view that handles all
//appropriate rendering contexts for use of common OpenGL
//commands.
//TO DO: Change name from SliceView to View
using System;
using CsGL.OpenGL;
using SampleGUI;
using System.ComponentModel;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
//TAKE OUT
public enum VIEW_TYPE
{
SAGITTAL, //view the yz plane
CORONAL, //view the xz plane
TRANSVERSE, //view the xy plane
OBLIQUE, //non-standard view
}
public enum DRAW_QUALITY
{
LOW, //draw pixel data using a bitmap and glDrawPixels
HIGH, //draw gridded data smoothly using GL_QUADS
}
public enum AXIS
{
X,
Y,
Z
}
public class SliceView : OpenGLControl
{
//Attributes//
private double[] volume = new double[3]; //3D working volume
private int[] planarAxes = new int[2];
private int orthoAxis;
//TO DO: add scale and rotate factors that correlate
//pixels moved to amount zoomed or rotated
private bool mouseDownLeft;
private bool mouseDownRight;
private float xRot, yRot;
private Point mousePos;
private Point zoomFocus = new Point(0,0);
public bool IsRotatable; //TO DO: Make this a property
private bool isRotated;
private bool isRotating = false;
private bool isZooming = false;
public bool IsZoomable; //TO DO: Make this a property
private float zoomScale = 1.0f;
private ElementIndexer3D indexer;
private ElementIndexer3D sizeIndexer;
private SliceDrawer sliceDrawer = new SliceDrawer();
//TAKE THIS OUT
public Label lbl = new Label();
public Scan scan;
public Eye eye;
private bool scanLoaded;
public int sliceIndex;
public bool DisplayFromBuffer = false;
public bool WriteToBuffer = false;
private ushort[] pixelArray;
//public DRAW_QUALITY DrawQuality;
private int xidx, yidx, zidx; //connects axes with array indices
//Properties
private VIEW_TYPE viewType;
public VIEW_TYPE ViewType
{
get
{
return viewType;
}
set
{
viewType = value;
AssignAxesToArrayIndices(); //assign x,y,z axes
//to [i,j,k] indices
}
}
private DRAW_QUALITY drawQuality;
public DRAW_QUALITY DrawQuality
{
get
{
return drawQuality;
}
set
{
drawQuality = value;
InitGLContext();
OnSizeChanged(null);
glDraw();
this.Refresh();
}
}
public float[] Color = new float[3];
public SliceView()
{
scanLoaded = false;
//set ortho view property
eye = new Eye();
viewType = VIEW_TYPE.TRANSVERSE;
DrawQuality = DRAW_QUALITY.LOW;
//ViewType = VIEW_TYPE.ORTHO_2D;
volume[0] = volume[1] = volume[2] = 5;
AssignAxesToArrayIndices();
this.MouseDown += new MouseEventHandler(
SliceView_MouseDown );
this.MouseUp += new MouseEventHandler(
SliceView_MouseUp);
this.MouseMove += new MouseEventHandler(
SliceView_MouseMove);
this.IsRotatable = true;
xRot = yRot = 0;
//create an indexer to convert between row-colum-plane to x-y-z
indexer = new ElementIndexer3D(AXIS_ALIGN.POSITIVE_Y,
AXIS_ALIGN.POSITIVE_X,AXIS_ALIGN.NEGATIVE_Z);
sizeIndexer = new ElementIndexer3D(AXIS_ALIGN.POSITIVE_Y,
AXIS_ALIGN.POSITIVE_X,AXIS_ALIGN.POSITIVE_Z);
}
//TO DO: change LoadScan to LinkToScan or make a property
public void LoadScan(Scan s)
{
scan = s;
scanLoaded = true;
//TO DO: make an easier way to recast arrays (might change to xyzposition class)
sizeIndexer.RCPIndex = scan.DimensionSize;
float[] startIndex = new float[3];
startIndex[0] = 0;
startIndex[1] = 0;
startIndex[2] = -sizeIndexer.Z;
eye.Index = startIndex;
int[] volume = new int[3];
volume = sizeIndexer.XYZIndex;
float[] volumef = new float[3];
for(int i = 0; i < 3; i++)
volumef[i] = (float) volume[i];
eye.Volume = volumef;
AssignAxesToArrayIndices();
eye.lbl = this.lbl;
}
public override void glDraw()
{
GL.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
GL.glShadeModel(GL.GL_SMOOTH);
if(scanLoaded)
{
if(DrawQuality.Equals(DRAW_QUALITY.LOW))
{
if(IsRotatable)
DrawUnsmoothSlice((AXIS)viewType);
else
DrawUnsmooth();
}
else
{
if(isRotated)
{
if(isRotating || isZooming)
{
DrawUnsmoothSlice((AXIS)VIEW_TYPE.TRANSVERSE);
DrawUnsmoothSlice((AXIS)VIEW_TYPE.CORONAL);
DrawUnsmoothSlice((AXIS)VIEW_TYPE.SAGITTAL);
}
else
{
DrawSmoothSlice((AXIS)VIEW_TYPE.TRANSVERSE);
DrawSmoothSlice((AXIS)VIEW_TYPE.CORONAL);
DrawSmoothSlice((AXIS)VIEW_TYPE.SAGITTAL);
}
}
else
{
if(isZooming)
DrawUnsmoothSlice((AXIS)viewType);
else
DrawSmoothSlice((AXIS)viewType);
}
}
}
}
protected override void InitGLContext()
{
GL.glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
GL.glEnable( GL.GL_DEPTH_TEST );
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
//add this so that nothing happens when the window
//is minimized (i.e. Size = 0) b/c I get an error
//if(this.Size.Width.Equals(0) || this.Size.Height.Equals(0))
// return;
}
protected void AssignAxesToArrayIndices()
{
switch(viewType)
{
//TAKE OUT xidx yidx zidx
case VIEW_TYPE.TRANSVERSE:
xidx = 1; yidx = 0; zidx = 2;
eye.SetViewingSide(VIEWING_SIDE.TOP);
break;
case VIEW_TYPE.SAGITTAL:
xidx = 0; yidx = 2; zidx = 1;
eye.SetViewingSide(VIEWING_SIDE.LEFT);
break;
case VIEW_TYPE.CORONAL:
xidx = 1; yidx = 2; zidx = 0;
eye.SetViewingSide(VIEWING_SIDE.FRONT);
break;
default:
xidx = 1; yidx = 0; zidx = 2;
break;
}
this.Refresh();
}
//******** DRAWING FUNCTIONS*******************//
private void DrawUnsmooth()
{
//GL.glDisable(GL.GL_DEPTH_TEST);
//GL.glMatrixMode(GL.GL_PROJECTION);
GL.glLoadIdentity();
GL.gluOrtho2D(0 , this.Width, 0, this.Height);
GL.glMatrixMode( GL.GL_MODELVIEW );
GL.glLoadIdentity();
int dimX = scan.DimensionSize[xidx];
int dimY = scan.DimensionSize[yidx];
int[] arrayIdx = new int[3];
arrayIdx[zidx] = scan.SliceIndex[zidx];
ushort[] pa = new ushort[dimX*dimY*3];
for(int i = 0; i < dimY; i++)
{
for(int j = 0; j < dimX; j++)
{
arrayIdx[xidx] = j;
arrayIdx[yidx] = i;
pa[(i*dimX+j)*3] = scan.Data[arrayIdx[0],arrayIdx[1],arrayIdx[2]];
pa[(i*dimX+j)*3+1] = scan.Data[arrayIdx[0],arrayIdx[1],arrayIdx[2]];
pa[(i*dimX+j)*3+2] = scan.Data[arrayIdx[0],arrayIdx[1],arrayIdx[2]];
}
}
int window = scan.Window;
int level = scan.Level;
//scale color components
GL.glPixelTransferf(GL.GL_RED_BIAS,-(level-window/2)/
(float)window);
GL.glPixelTransferf(GL.GL_GREEN_BIAS,-(level-window/2)/
(float)window);
GL.glPixelTransferf(GL.GL_BLUE_BIAS,-(level-window/2)/
(float)window);
GL.glPixelTransferi(GL.GL_RED_SCALE,65536/window);
GL.glPixelTransferi(GL.GL_GREEN_SCALE,65536/window);
GL.glPixelTransferi(GL.GL_BLUE_SCALE,65536/window);
//unpack so that image dimensions
//do NOT have to be multiple of 4
GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT,1);
//zoom so that image takes up entire viewport
float zoomx, zoomy;
zoomx = this.Size.Width/(float)dimX;
zoomy = this.Size.Height/(float)dimY;
//set raster position
switch(this.viewType)
{
case VIEW_TYPE.TRANSVERSE:
GL.glRasterPos2f(-1f,-1f);
break;
case VIEW_TYPE.SAGITTAL:
zoomy *= -1;//flip sagittal y view
zoomx *= -1;//flip sagittal x view
GL.glRasterPos2f(1f,1f);
break;
case VIEW_TYPE.CORONAL:
zoomy *= -1;//flip coronal y view
GL.glRasterPos2f(-1f,1f);
break;
default: break;
}
//set zoom
GL.glPixelZoom(zoomx,zoomy);
//Draw from the buffer
GL.glDrawPixels(dimX,dimY, GL.GL_RGB, GL.GL_UNSIGNED_SHORT,pa);
GL.glFlush();
}
private void RotateScene()
{
if(this.IsRotatable && this.isRotated)
{
//get coordinates of last element
indexer.RCPIndex = scan.DimensionSize;
//back translate
GL.glTranslatef(indexer.X/2.0f,indexer.Y/2.0f,indexer.Z/2.0f);
//rotate in x relative to camera
GL.glRotatef(xRot,eye.UpVector[0],eye.UpVector[1],
eye.UpVector[2]);
//rotate in y relative to camera
GL.glRotatef(yRot,eye.RightVector[0],eye.RightVector[1],
eye.RightVector[2]);
//forward translate
GL.glTranslatef(-indexer.X/2.0f,-indexer.Y/2.0f,-indexer.Z/2.0f);
}
}
private void DrawSmoothSlice(AXIS normalAxis)
{
sliceDrawer.Indexer = indexer; //TO DO: PUT THIS IN LOAD SCAN
eye.SetView();
this.RotateScene();
sliceDrawer.DrawSlice(scan,(int)normalAxis);
}
private void DrawUnsmoothSlice(AXIS normalAxis)
{
//define axis that are coplanar with the slice
AXIS[] planarAxes = new AXIS[2];
int idx = 0;
for(int i = 0; i < 3; i++)
{
if(i != (int)normalAxis)
{
planarAxes[idx] = (AXIS)i;
idx++;
}
}
//get size in x,y,z
sizeIndexer.RCPIndex = scan.DimensionSize;
indexer.RCPIndex = scan.DimensionSize;
int dim1 = sizeIndexer.GetXYZElement((int)planarAxes[0]);
int dim2 = sizeIndexer.GetXYZElement((int)planarAxes[1]);
float gray;
int window = scan.Window;
int level = scan.Level;
GL.glEnable(GL.GL_DEPTH_TEST);
GL.glMatrixMode(GL.GL_MODELVIEW);
GL.glLoadIdentity();
GL.glOrtho(eye.Left,eye.Right,eye.Bottom,eye.Top,
eye.Near,eye.Far);
GL.gluLookAt(eye.Position[0],eye.Position[1],eye.Position[2],
eye.Target[0],eye.Target[1],eye.Target[2],
eye.UpVector[0],eye.UpVector[1],eye.UpVector[2]);
//rotate
if(this.IsRotatable && this.isRotated)
{
//find the location of the last element so we can translate to the center
indexer.RCPIndex = scan.DimensionSize;
//back translate
GL.glTranslatef(indexer.X/2.0f,indexer.Y/2.0f,indexer.Z/2.0f);
//rotate in x relative to camera
GL.glRotatef(xRot,eye.UpVector[0],eye.UpVector[1],
eye.UpVector[2]);
//rotate in y relative to camera
GL.glRotatef(yRot,eye.RightVector[0],eye.RightVector[1],
eye.RightVector[2]);
//forward translate
GL.glTranslatef(-indexer.X/2.0f,-indexer.Y/2.0f,-indexer.Z/2.0f);
}
indexer.RCPIndex = scan.SliceIndex;
GL.glPointSize(this.Width/250f*zoomScale*3f);
for(int i = 0; i < dim1-1; i++)
{
for(int j = 0; j < dim2-1; j++)
{
GL.glBegin(GL.GL_POINTS);
indexer.SetXYZElement((int)planarAxes[0],i);
indexer.SetXYZElement((int)planarAxes[1],j);
gray = scan.Data[indexer.Row,indexer.Column,indexer.Plane];
gray = (gray-(level-window/2))/(float)window;
GL.glColor3f(gray,gray,gray);
GL.glVertex3f(indexer.X,indexer.Y,indexer.Z);
GL.glEnd();
}
}
}
//EVENT HANDLING------------------------------------------------------------//
//handle mouse down event
//TO DO: add mouse window and level
private void SliceView_MouseDown( object sender, MouseEventArgs e)
{
if(e.Button.Equals(MouseButtons.Left) && IsRotatable)
{
mouseDownLeft = true;
mousePos.X = e.X;
mousePos.Y = e.Y;
}
if(e.Button.Equals(MouseButtons.Right) && IsZoomable)
{
mouseDownRight = true;
mousePos.X = e.X; //record mouse pos so we can measure distance moved
mousePos.Y = e.Y; //record mouse pos so we can measure distance moved
//also record this point as the focus of the zoom
float[] pos = new float[2];
pos[0] = e.X/(float)this.Width*(eye.Right - eye.Left) + eye.Left; //x zoom focus
pos[1] = e.Y/(float)this.Height*(eye.Bottom - eye.Top) + eye.Top; //y zoom focus
eye.ZoomFocus = pos;
eye.SetZoom(zoomScale);
}
}
//handle mouse up event
private void SliceView_MouseUp( object sender, MouseEventArgs e)
{
mouseDownLeft = false;
mouseDownRight = false;
if(isRotating)
{
isRotating = false;
this.Refresh();
}
if(isZooming)
{
isZooming = false;
this.Refresh();
}
}
//handle mouse move event
private void SliceView_MouseMove( object sender, MouseEventArgs e)
{
if(IsRotatable && mouseDownLeft)
{
isRotated = true;
isRotating = true;
xRot += e.X - mousePos.X;
yRot += e.Y - mousePos.Y;
if(xRot > 360)
xRot -= 360;
if(xRot < -360)
xRot += 360;
if(yRot > 360)
yRot -= 360;
if(yRot < -360)
yRot += 360;
mousePos.X = e.X;
mousePos.Y = e.Y;
this.Refresh();
}
if(IsZoomable && mouseDownRight)
{
isZooming = true;
if(e.Y < mousePos.Y)
zoomScale += 1f*(float)Math.Pow((e.Y - mousePos.Y),1)/(float)this.Height;
else
zoomScale += 1f*(float)Math.Pow((e.Y - mousePos.Y),1)/(float)this.Height;
mousePos.X = e.X;
mousePos.Y = e.Y;
eye.SetZoom( zoomScale );
zoomScale = eye.ZoomScale; //in case we've exceeded the min or max
this.Refresh();
int[] tmp = {5,5,5};
eye.Volume = tmp;
}
}
}